const express = require("express");
const http = require("http");
const io = require("socket.io");
const updateCompiler = require("./utils/updateCompiler");
const webpackDevMiddleware = require("../../webpack-dev-middleware/index");
class Server {
  constructor(compiler, devServerArgs) {
    this.compiler = compiler;
    this.devServerArgs = devServerArgs;
    this.sockets = [];

    // 给 entry 添加客户端放在初次请求的 man chunk 中
    updateCompiler(compiler);

    // 添加 done hooks 钩子， 获取编译后的信息
    this.setupHooks();

    // 设置app 创建 http 服务，设置文件系统的根目录
    this.setupApp();
    this.routes();
    this.createServer();

    // express 拦截中间件 用来预览生成的文件
    this.setupDevMiddleware();

    this.createSocketServer();
  }
  setupDevMiddleware() {
    this.middleware = webpackDevMiddleware(this.compiler);
    this.app.use(this.middleware);
  }
  setupHooks() {
    // 监听编译成功的事件
    // 当 webpack 完成一次编译之后， 会触发 done 这个钩子回调函数执行
    // stats 中包含 modules、chunks、files、assets、entries
    this.compiler.hooks.done.tap("webpack-dev-server", (stats) => {
      console.log("done 新的编译完成 新的 hash 为", stats.hash);

      this.sockets.forEach((socket) => {
        socket.emit("hash", stats.hash);
        socket.emit("ok");
      });

      this._stats = stats; // 保存上一次的 stats 信息
    });
  }

  createSocketServer() {
    const websocketServer = io(this.server);

    websocketServer.on("connection", (socket) => {
      console.log("一个新的 websocket 客户端已经连接上了");
      this.sockets.push(socket);

      socket.on("disconnect", () => {
        const index = this.sockets.indexOf(socket);
        this.sockets.splice(index, 1);
      });

      if (this._stats) {
        this.sockets.forEach((socket) => {
          socket.emit("hash", this._stats.hash);
          socket.emit("ok");
        });
      }
    });
  }

  routes() {
    if (this.devServerArgs.contentBase) {
      // 此目录将会作为静态文件的根目录
      this.app.use(express.static(this.devServerArgs.contentBase));
    }
  }

  setupApp() {
    // this app 并不是一个 http 服务， 本身是一个路由中间件
    this.app = express();
  }
  createServer() {
    this.server = http.createServer(this.app);
  }
  listen(port, host, callback) {
    this.server.listen(port, host, callback);
  }
}

module.exports = Server;
